/*
Copyright (c) 2009, Luke Berndt
All rights reserved.
 
 * More Information: http://www.tagflo.com

Redistribution and use in source and binary forms, with or without modification, are permitted 
provided that the following conditions are met:

* Redistributions of source code must retain the above copyright notice, this list of conditions 
  and the following disclaimer.

* Redistributions in binary form must reproduce the above copyright notice, this list of conditions 
  and the following disclaimer in the documentation and/or other materials provided with the distribution.

* Neither the name of RoboTastic Software nor the names of its contributors may be used to endorse or 
  promote products derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED 
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
POSSIBILITY OF SUCH DAMAGE.
*/


using System;
using System.Diagnostics;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Text;
using System.IO;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Data.SqlServerCe;
using System.Data.OleDb;
using System.Data.SqlClient;
using System.Data;

namespace TagFlo
{
    class MetaDataEditor
    {

        private SqlCeConnection conn;
        private SqlCeDataAdapter photoAdapter;
        private SqlCeDataAdapter tagAdapter;
        private String path;


        private uint paddingAmount = 1048; // 2Kb padding for this example, but really this can be any value. 
        // Our recommendation is to keep this between 1Kb and 5Kb as most metadata updates are not large.
        DataSet dataSet;
        string fileName = "SQLPhotoDB.sdf";
        string password = "";

        private String ConnectString()
        {
            return string.Format("DataSource=\"{0}\"; Password='{1}'", fileName, password);
        }

        public void SqlDataSet()
        {

            if (conn.State == ConnectionState.Closed)
            {
                conn.Open();
            }

            dataSet = new DataSet();

            String selectString = "SELECT * FROM PhotoTable WHERE Path='" + path + "';"; // "' AND Path='" + Path.GetFullPath(path) + "';";
            photoAdapter = new SqlCeDataAdapter(selectString, conn);
            SqlCeCommandBuilder photoBuilder = new SqlCeCommandBuilder(photoAdapter);
            photoBuilder.QuotePrefix = "[";
            photoBuilder.QuoteSuffix = "]";

            photoAdapter.Fill(dataSet, "PhotoTable");

            tagAdapter = new SqlCeDataAdapter("SELECT * FROM TagTable", conn);
            SqlCeCommandBuilder tagBuilder = new SqlCeCommandBuilder(tagAdapter);
            tagBuilder.QuotePrefix = "[";
            tagBuilder.QuoteSuffix = "]";
            
            tagAdapter.Fill(dataSet, "TagTable");
        }

        public MetaDataEditor(String p, SqlCeConnection c)
        {
            path = p;
            conn = c;
            SqlDataSet();
        }

        ~MetaDataEditor()
        {
            
        }


        private void ChangeSqlValue(String element, Object value)
        {
            switch (element)
            {
                case "Tag":

                    string[] split;
                    string[] stringSeparators = new string[] { ", " };
                  List<string> tagList;


                    string tags = dataSet.Tables["PhotoTable"].Rows[0]["Tags"].ToString();
                    split = (value as String).Split(stringSeparators, StringSplitOptions.RemoveEmptyEntries);
                    tagList = new List<string>((tags as String).Split(stringSeparators, StringSplitOptions.RemoveEmptyEntries));
                    
                    foreach (string s in split)
                    {
                       if (tagList.BinarySearch(s) < 0)
                       {
                           tagList.Add(s);
                            DataRow row = dataSet.Tables["TagTable"].NewRow();
                            row["Path"] = path;
                            row["Tag"] = s;
                            dataSet.Tables["TagTable"].Rows.Add(row);
                        }
                    }

                    String val = "";
                    try
                    {

                        foreach (string tag in tagList)
                        {
                            val = val + tag + ", ";
                        }
                    }
                    catch (Exception ex)
                    {
                        Trace.WriteLine("Error creating Tag string: " + val + " Error: " + ex.Message);
                    }
                    string test=tagList.ToString();

                    dataSet.Tables["PhotoTable"].Rows[0]["Tags"] = val;
                    
                    break;
                case "Title":
                    dataSet.Tables["PhotoTable"].Rows[0]["Title"] = (value as string);
                  
                    break;
                case "Subject":
                    dataSet.Tables["PhotoTable"].Rows[0]["Subject"] = (value as string);
                    break;
                case "Comment":
                    dataSet.Tables["PhotoTable"].Rows[0]["Comment"] = (value as string);
                    break;
                case "Rating":
                    dataSet.Tables["PhotoTable"].Rows[0]["Rating"] = (int) value;
                    break;
            }
            photoAdapter.Update(dataSet.Tables["PhotoTable"]);
            tagAdapter.Update(dataSet.Tables["TagTable"]);
        
        }
        private void ChangeValue(BitmapMetadata meta, String element, Object value)
        {
            switch (element)
            {
                case "Tag":

                    string[] split;
                    string[] stringSeparators = new string[] { ", " };
                    List<string> tagList;

                    if (meta.Keywords != null)
                    {
                        tagList = new List<string>(meta.Keywords);
                    }
                    else
                    {
                        tagList = new List<string>();
                    }
                    split = (value as String).Split(stringSeparators, StringSplitOptions.RemoveEmptyEntries);
                    foreach (string s in split)
                    {
                        if (tagList.BinarySearch(s) < 0)
                        {
                            tagList.Add(s);
                        }
                    }
                    
                    String val = "";
                    try
                    {

                        foreach (string tag in tagList)
                        {
                            val = val + tag + ", ";
                        }
                    }
                    catch (Exception ex)
                    {
                        Trace.WriteLine("Error creating Tag string: " + val + " Error: " + ex.Message);
                    }


                    meta.Keywords = new ReadOnlyCollection<string>(tagList);

                    break;
                case "Title":
                    meta.Title = (value as string);
                    break;
                case "Subject":
                    meta.Subject = (value as string);
                    break;
                case "Comment":
                    meta.Comment = (value as string);
                    break;
                case "Rating":
                    meta.Rating = (int)value;
                    break;
            }
        }



        private Boolean EditInplace(String element, Object value)
        {
            Boolean success = false;
            Stream file = File.Open(path, FileMode.Open, FileAccess.ReadWrite);
            BitmapDecoder output = BitmapDecoder.Create(file, BitmapCreateOptions.None, BitmapCacheOption.Default);
            InPlaceBitmapMetadataWriter metadata = output.Frames[0].CreateInPlaceBitmapMetadataWriter();
            
            ChangeValue(metadata, element, value);

            /*
            Console.WriteLine("Padding First: " + metadata.GetQuery("/app1/ifd/PaddingSchema:Padding") +
            " Padding Second: " + metadata.GetQuery("/app1/ifd/exif/PaddingSchema:Padding") +
            " Padding Third: " + metadata.GetQuery("/xmp/PaddingSchema:Padding"));
            */
            try
            {

                if (metadata.TrySave())
                {
                    success = true;

                }
                else
                {
                    success = false;
                }
            }
            catch (Exception ex)
            {
                Trace.WriteLine(ex.Message);
            }
            file.Close();
            return success;
        }

        private  void EditFullCopy(String element, Object value)
        {

                Stream file = File.Open(path, FileMode.Open, FileAccess.ReadWrite);
                BitmapCreateOptions createOptions = BitmapCreateOptions.PreservePixelFormat | BitmapCreateOptions.IgnoreColorProfile;

                BitmapDecoder original = BitmapDecoder.Create(file, createOptions, BitmapCacheOption.Default); //BitmapCacheOption.None);

                if (!original.CodecInfo.FileExtensions.Contains("jpg"))
                {
                    Trace.WriteLine("The file you passed in is not a JPEG.");
                    return;
                }

                JpegBitmapEncoder output = new JpegBitmapEncoder();
                BitmapMetadata metadata = original.Frames[0].Metadata.Clone() as BitmapMetadata;

               metadata.SetQuery("/app1/ifd/PaddingSchema:Padding", paddingAmount);
               metadata.SetQuery("/app1/ifd/exif/PaddingSchema:Padding", paddingAmount);
               metadata.SetQuery("/xmp/PaddingSchema:Padding", paddingAmount);
            
               ChangeValue(metadata, element, value);
            
               BitmapFrame bFrame = BitmapFrame.Create(original.Frames[0], original.Frames[0].Thumbnail, metadata, original.Frames[0].ColorContexts);
       
                   output.Frames.Add(bFrame);
            

                   using (Stream outputFile = File.Open(System.IO.Path.GetDirectoryName(path) + "\\temp.jpg", FileMode.Create, FileAccess.ReadWrite))
                   {
                   
                       output.Save(outputFile);
                        }
               
                file.Close();
 
        }

        public void Edit(String element, Object value)
        {
            Boolean inPlaceSuccess;

            ChangeSqlValue(element, value);

            inPlaceSuccess = EditInplace(element, value);

            if (!inPlaceSuccess)
            {
                Trace.WriteLine("Edit inplace failed on file");
                EditFullCopy(element, value);
                if (File.Exists(path))
                {
                    File.Delete(path);
                }
                File.Move(System.IO.Path.GetDirectoryName(path) + "\\temp.jpg", path);
                

            }
           
        }


        /*
        public string Comment
        {
            get
            {
                string val = jpegInplace.Comment;
                return (val != null ? (string)val : String.Empty);
            }

            set
            {
                if (jpegInplace.TrySave() == true)
                {
                    jpegInplace.Comment = value;
                }
            }

        }*/

        /*
        public string Title
        {
            get
            {
                string val = jpegInplace.Title;
                return (val != null ? (string)val : String.Empty);
            }
            set
            {
                if (jpegInplace.TrySave() == true)
                {
                    jpegInplace.Title = value;
                }
            }

        }
*/
        /*
        public int Rating
        {
            get
            {
                int val = jpegInplace.Rating;
                return val;
            }
            set
            {
                if (jpegInplace.TrySave() == true)
                {
                    jpegInplace.Rating = value;
                }
            }

        }
*/

        /*
        public string Subject
        {
            get
            {
                string val = jpegInplace.Subject;
                return (val != null ? (string)val : String.Empty);
            }
            set
            {
                if (jpegInplace.TrySave() == true)
                {
                    jpegInplace.Subject = value;
                }
            }

        }
*/

        /*
        public string Location
        {
            get
            {
                string val = jpegInplace.Location;
                return (val != null ? (string)val : String.Empty);
            }
           

        }
        */

        /*
        public ReadOnlyCollection<string> TagList
        {
            get
            {
                ReadOnlyCollection<string> tagsList = jpegInplace.Tags;
                return tagsList;
            }

        }


        */


        /*
        public String Tags
        {
            get
            {
                ReadOnlyCollection<string> tagsList = jpegInplace.Tags;
                string val = "";
                try
                {
                    foreach (string tag in tagsList)
                    {
                        val = val + tag + ", ";
                    }
                }
                catch (Exception ex)
                {
                }
                return (val != null ? (string)val : String.Empty);
            }
        }
        */
    }
}
